IDP 平台开发手册
本文档面向插件开发者,提供完整的 Zadig IDP 插件开发指南,帮助您快速构建和发布自定义插件。
# 什么是 IDP 插件系统
# IDP(内部开发者平台)概述
内部开发者平台(Internal Developer Platform, IDP)是为开发团队提供的统一工作平台,旨在:
- 降低认知负担:开发者无需关注底层基础设施,专注于业务开发
- 提升开发效率:提供标准化的工具链和自助服务能力
- 统一开发体验:整合分散的开发工具,提供一致的操作界面
- 沉淀最佳实践:将企业开发经验固化为可复用的能力
# 插件系统的价值
Zadig 的插件系统为构建企业级 IDP 提供了强大的扩展能力:
- 功能扩展:通过插件扩展系统功能,满足个性化需求
- 界面定制:自定义导航菜单和业务页面,提升用户体验
- 集成增强:集成第三方工具和服务,实现更丰富的功能集成
- 灵活配置:支持插件的启用和禁用,按需使用
# 应用场景
# 场景一:工具链集成
背景:
企业通常使用多个开发工具,开发者需要在不同系统间频繁切换,效率低下。
解决方案:
通过插件将常用工具集成到 Zadig 平台中:
- 代码审查集成:GitLab MR 查看器、GitHub PR 管理
- 质量监控集成:SonarQube 代码质量报告、测试覆盖率展示
- 监控告警集成:Prometheus 指标展示、Grafana 看板嵌入
- 协作工具集成:Jira 任务看板、飞书/钉钉消息中心
# 场景二:研发流程标准化
背景:
不同团队的开发流程不统一,新人上手成本高,协作困难。
解决方案:
通过插件固化企业研发流程:
- 发布审批流程:自定义审批插件,规范上线流程
- 代码规范检查:集成 Lint 工具,自动检查代码规范
- 文档模板插件:提供标准化的技术文档模板
- 环境申请插件:统一的测试环境申请和管理流程
# 场景三:数据洞察与决策
背景:
管理者缺乏研发效能数据,难以发现瓶颈和优化方向。
解决方案:
开发数据展示和分析插件:
- 研发效能看板:交付周期、部署频率、变更失败率等关键指标
- 团队健康度:代码提交活跃度、代码审查效率分析
- 资源使用分析:环境资源消耗、成本分析
- 质量趋势报告:Bug 趋势、测试通过率、技术债务分析
# 场景四:企业特色功能
背景:
每个企业都有独特的业务场景和管理需求,通用平台难以完全满足。
解决方案:
根据企业特色开发定制插件:
- 合规性检查:金融行业的安全审计、日志留存
- 多云管理:混合云环境的统一管理和切换
- 成本优化:云资源成本分析和优化建议
- 知识库集成:企业内部技术文档、最佳实践库
# 插件类型说明
Zadig 插件支持两种主要类型,满足不同的使用场景:
# 导航功能页(Page)
特点:
- 独立的功能页面,有完整的页面布局
- 出现在系统侧边栏菜单中
- 拥有独立的 URL 访问路径
适用场景:
- 需要完整页面展示的功能(如 MR 列表、数据看板)
- 功能相对独立,不依赖特定业务上下文
- 需要深度交互的复杂功能
示例:GitLab MR 查看器、研发效能看板、代码质量报告

# 标签页(Tab)
特点:
- 嵌入到现有页面的标签页组件
- 显示在业务目录的标签区域
- 依赖父页面的上下文信息
适用场景:
- 扩展现有页面功能(如项目详情页)
- 需要业务上下文的功能(如项目级别的质量报告)
- 轻量级的信息展示
示例:项目质量标签、环境配置标签、部署历史标签

# 开发环境准备
# 系统要求
开发 Zadig 插件需要以下环境:
- Node.js: >= 20.0.0
- npm: 随 Node.js 安装
- 现代浏览器: 支持 ES6+ 语法(Chrome、Firefox、Safari、Edge 等)
# 安装开发工具
# 全局安装 CLI 工具
# 全局安装 zadig-plugin-cli
npm install -g zadig-plugin-cli-dev
# 验证安装
zadig-plugin --version
zadig-plugin --help
2
3
4
5
6
# 快速开始
# 第一步:创建插件项目
根据需求创建不同类型的插件:
# 创建导航功能页插件
zadig-plugin create my-hello-plugin
# 创建标签页插件
zadig-plugin create my-tab-plugin --type tab
2
3
4
5
命令选项:
-t, --type <type>- 插件类型(page, tab),默认为 page-f, --force- 强制覆盖已存在的目录
# 第二步:安装依赖
# 进入项目目录
cd my-hello-plugin
# 安装依赖
npm install
2
3
4
5
# 第三步:开发调试
# 启动开发服务器
zadig-plugin dev
2
开发服务器启动后,可以在浏览器中实时查看插件效果和调试功能。修改代码后会自动热重载。
# 第四步:构建发布
# 构建生产版本
zadig-plugin build
2
构建完成后,会在项目目录生成 plugin.js 文件,可直接上传到 Zadig 系统使用。
# 插件开发实现
# Page 插件代码模板
入口文件结构:
// src/index.js
export default {
name: 'my-plugin', // 插件标识
displayName: '我的插件', // 显示名称
type: 'page', // 类型:page
route: '/my-plugin', // 路由路径
component: MyPluginPage // 主组件
};
2
3
4
5
6
7
8
完整示例:
// src/index.js
import './styles.css';
const MyPluginPage = {
name: 'MyPluginPage',
data() {
return {
data: [],
loading: true
};
},
async mounted() {
await this.fetchData();
},
methods: {
async fetchData() {
try {
const response = await fetch('/api/data');
const result = await response.json();
this.data = result;
} catch (error) {
console.error('Error fetching data:', error);
} finally {
this.loading = false;
}
}
},
template: `
<div class="my-plugin-container">
<h1>我的插件</h1>
<div v-if="loading">加载中...</div>
<div v-else class="content">
<!-- 业务逻辑 -->
</div>
</div>
`
};
export default {
name: 'my-plugin',
displayName: '我的插件',
type: 'page',
route: '/my-plugin',
component: MyPluginPage
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Tab 插件代码模板
入口文件结构:
// src/index.js
export default {
name: 'my-tab-plugin', // 插件标识
displayName: '自定义标签', // 标签显示名称
type: 'tab', // 类型:tab
component: MyTabComponent // 标签组件
};
2
3
4
5
6
7
完整示例:
// src/index.js
import './styles.css';
const MyTabComponent = {
name: 'MyTabComponent',
props: {
context: {
type: Object,
default: () => ({})
}
},
data() {
return {
info: null
};
},
watch: {
'context.projectName': {
handler(newProjectName) {
if (newProjectName) {
this.loadProjectInfo(newProjectName);
}
},
immediate: true
}
},
methods: {
async loadProjectInfo(name) {
try {
const response = await fetch(`/api/project/${name}`);
const data = await response.json();
this.info = data;
} catch (error) {
console.error('Error:', error);
}
}
},
template: `
<div class="my-tab-container">
<h3>项目信息</h3>
<div v-if="info" class="info-content">
<!-- 展示项目相关信息 -->
</div>
<div v-else>暂无数据</div>
</div>
`
};
export default {
name: 'my-tab-plugin',
displayName: '项目信息',
type: 'tab',
component: MyTabComponent
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 项目结构规范
# 标准项目结构
my-plugin/
├── src/
│ ├── index.js # 插件入口文件(必需)
│ ├── App.vue # 主组件
│ ├── components/ # 组件目录
│ │ ├── Header.vue
│ │ └── List.vue
│ ├── utils/ # 工具函数
│ │ ├── api.js
│ │ └── storage.js
│ ├── styles/ # 样式文件
│ │ └── index.css
│ └── constants/ # 常量定义
│ └── config.js
├── package.json # 项目配置
├── plugin.config.js # 插件配置(必需)
└── README.md # 说明文档
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 插件配置文件
plugin.config.js 用于配置插件的基本信息:
module.exports = {
name: 'my-plugin', // 插件唯一标识
displayName: '我的插件', // 显示名称
type: 'page', // 插件类型: page 或 tab
route: '/my-plugin', // 路由路径(仅 page 类型需要)
description: '插件描述', // 插件功能描述
version: '1.0.0', // 版本号
author: 'Your Name', // 作者信息(可选)
icon: 'icon-name' // 图标名称(可选)
};
2
3
4
5
6
7
8
9
10
# 入口文件规范
src/index.js 是插件的入口文件:
import Vue from 'vue';
import App from './App.vue';
import './styles/index.css';
// 导出插件配置和组件
export default {
// 插件元数据
name: 'my-plugin',
displayName: '我的插件',
type: 'page',
// 插件组件
component: App,
// 生命周期钩子(可选)
onMount: () => {
console.log('插件已加载');
},
onUnmount: () => {
console.log('插件已卸载');
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 开发规范与最佳实践
# 1. 代码规范
# 组件开发
// 使用Vue2组件和Options API
export default {
name: 'MyComponent',
data() {
return {
data: []
};
},
async mounted() {
// 数据加载逻辑
fetchData();
},
template: `<div><!-- 组件内容 --></div>`
};
2
3
4
5
6
7
8
9
10
11
12
13
14
# 样式管理
/* 使用 CSS 模块化或统一前缀避免样式冲突 */
.my-plugin-container {
padding: 20px;
}
.my-plugin-header {
font-size: 18px;
font-weight: bold;
}
2
3
4
5
6
7
8
9
# 2. 响应式设计
插件应适配不同屏幕尺寸:
/* 移动端适配 */
@media (max-width: 768px) {
.my-plugin-container {
padding: 10px;
}
}
/* 平板适配 */
@media (min-width: 769px) and (max-width: 1024px) {
.my-plugin-container {
padding: 15px;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 3. 错误处理
做好异常处理,提供友好的错误提示:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('数据加载失败');
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
// 显示错误提示给用户
showErrorMessage('加载数据时出错,请稍后重试');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4. 性能优化
# 懒加载
// 使用Vue异步组件进行懒加载
export default {
name: 'App',
components: {
HeavyComponent: () => import('./HeavyComponent.vue')
},
template: `
<div>
<HeavyComponent />
</div>
`
};
2
3
4
5
6
7
8
9
10
11
12
# 防抖与节流
// 使用 debounce 优化搜索输入
export default {
name: 'SearchInput',
data() {
return {
query: '',
timer: null
};
},
methods: {
handleSearch(value) {
// 清除上一次的定时器
if (this.timer) {
clearTimeout(this.timer);
}
// 设置新的定时器
this.timer = setTimeout(() => {
this.performSearch(value);
}, 300);
},
performSearch(value) {
// 执行搜索逻辑
console.log('搜索:', value);
}
},
template: `
<input
v-model="query"
@input="handleSearch(query)"
placeholder="输入搜索关键词"
/>
`
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 5. 用户体验
# 加载状态
export default {
name: 'DataList',
data() {
return {
loading: true,
data: []
};
},
template: `
<div>
<div v-if="loading" class="loading-spinner">
加载中...
</div>
<div v-else>
<!-- 数据展示 -->
</div>
</div>
`
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 空状态提示
export default {
name: 'DataList',
props: {
data: {
type: Array,
default: () => []
}
},
methods: {
handleRefresh() {
this.$emit('refresh');
}
},
template: `
<div>
<div v-if="data.length === 0" class="empty-state">
<p>暂无数据</p>
<button @click="handleRefresh">刷新</button>
</div>
<div v-else>
<!-- 数据列表 -->
</div>
</div>
`
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 6. 数据持久化
使用浏览器本地存储保存用户配置:
// 工具函数
const storage = {
set(key, value) {
localStorage.setItem(
`zadig-plugin-${key}`,
JSON.stringify(value)
);
},
get(key) {
const value = localStorage.getItem(`zadig-plugin-${key}`);
return value ? JSON.parse(value) : null;
},
remove(key) {
localStorage.removeItem(`zadig-plugin-${key}`);
}
};
// 使用示例
function saveConfig(config) {
storage.set('config', config);
}
function loadConfig() {
return storage.get('config') || {};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# API 调用规范
# 调用 Zadig API
// API 调用封装
class ZadigAPI {
constructor() {
this.baseURL = window.location.origin;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}/api/aslan${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
},
credentials: 'include' // 携带认证信息
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
// 获取项目列表
async getProjects() {
return this.request('/api/aslan/project');
}
// 获取工作流列表
async getWorkflows(projectName) {
return this.request(`/api/aslan/workflow/v4?projectName=${projectName}`);
}
}
export default new ZadigAPI();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 调用外部 API
// 调用外部服务 API
async function fetchGitLabMRs(gitlabUrl, token) {
const response = await fetch(`${gitlabUrl}/api/v4/merge_requests`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch GitLab MRs');
}
return response.json();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 调试技巧
# 1. 开发环境调试
# 启动开发服务器时查看详细日志
zadig-plugin dev --verbose
# 指定端口
zadig-plugin dev --port 3000
2
3
4
5
# 3. 日志输出
// 开发环境日志
if (process.env.NODE_ENV === 'development') {
console.log('Debug info:', data);
}
// 统一日志管理
const logger = {
info: (message, data) => {
console.log(`[INFO] ${message}`, data);
},
error: (message, error) => {
console.error(`[ERROR] ${message}`, error);
},
warn: (message, data) => {
console.warn(`[WARN] ${message}`, data);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 插件发布
# 构建生产版本
# 执行构建
zadig-plugin build
# 验证构建产物
ls -lh plugin.js
2
3
4
5
# 上传到 Zadig
- 登录 Zadig 系统
- 进入 系统设置 → 插件管理
- 点击 新建插件
- 填写插件信息:
- 插件名称: 显示名称
- 插件类型: 选择 page 或 tab
- 路由路径: URL 路径(page 类型)
- 插件描述: 功能说明
- 插件状态: 启用/禁用
- 上传
plugin.js文件 - 点击 创建插件
# 版本管理
建议遵循语义化版本规范:
- 主版本号: 不兼容的 API 修改
- 次版本号: 向下兼容的功能性新增
- 修订号: 向下兼容的问题修正
// plugin.config.js
module.exports = {
name: 'my-plugin',
version: '1.2.3', // 主.次.修订
// ...
};
2
3
4
5
6
# 插件市场
官方插件库
GitHub: https://github.com/koderover/zadig-idp-plugins
欢迎贡献和分享您的插件
# GitLab MR 查看器
官方提供的示例插件,展示如何集成外部系统。
功能特性:
- 查看分配给我的 Merge Request
- 按状态和范围筛选 MR
- 支持 GitLab.com 和私有实例
- 本地配置管理,数据安全
获取方式:
# 从官方插件库下载
git clone https://github.com/koderover/zadig-idp-plugins.git
cd zadig-idp-plugins/gitlab-mr-viewer
# 构建插件
npm install
npm run build
# 上传 plugin.js 到 Zadig
2
3
4
5
6
7
8
9
配置说明:
- 获取 GitLab Personal Access Token(需要
api和read_user权限) - 在插件配置页面填写 GitLab URL 和 Token
- 测试连接成功后保存配置
更多插件: 访问官方插件库查看更多示例:https://github.com/koderover/zadig-idp-plugins
# 常见问题
# 构建相关
# Q: 构建失败,提示 Node 版本不兼容?
A: 检查并升级 Node.js 版本:
# 检查当前版本
node --version
# 使用 nvm 升级(推荐)
nvm install 20
nvm use 20
# 重新安装依赖
rm -rf node_modules package-lock.json
npm install
2
3
4
5
6
7
8
9
10
# Q: 构建产物过大怎么办?
A: 优化构建配置:
- 移除未使用的依赖
- 使用代码分割(code splitting)
- 压缩资源文件
- 检查是否误打包了开发依赖
# 开发相关
# Q: 开发服务器启动失败?
A: 排查步骤:
- 确认在正确的插件项目目录中
- 检查
package.json和plugin.config.js是否存在 - 确保已安装项目依赖:
npm install - 检查端口是否被占用,尝试指定其他端口:
zadig-plugin dev --port 3001
# Q: 插件在 Zadig 中无法加载?
A: 检查清单:
- 验证
plugin.js文件是否正确生成 - 检查浏览器控制台是否有 JavaScript 错误
- 确认插件配置信息是否完整
- 检查插件类型和路由配置是否正确
# Q: 如何调试已上传的插件?
A:
- 打开浏览器开发者工具
- 在 Console 标签查看错误日志
- 在 Network 标签监控 API 请求
- 如需修改,重新构建并上传新版本
# API 相关
# Q: 调用 Zadig API 提示权限错误?
A:
- 确保请求中包含认证信息:
credentials: 'include' - 检查当前用户是否有相应的权限
- 验证 API endpoint 路径是否正确
# Q: 跨域请求被阻止?
A:
- Zadig API 调用应使用相对路径,避免跨域问题
- 外部 API 调用需要目标服务器支持 CORS
- 可以考虑通过 Zadig 后端代理外部 API
# 样式相关
# Q: 插件样式与系统样式冲突?
A:
- 使用唯一的 CSS 类名前缀,如
.my-plugin-* - 考虑使用 CSS Modules
- 避免使用全局样式
- 使用 CSS-in-JS 方案(如 styled-components)
# 开发资源
# 官方资源
- 插件库: https://github.com/koderover/zadig-idp-plugins(opens new window)
- 示例代码: 官方插件库中包含多个完整示例
- API 文档: 参考 Zadig OpenAPI 文档
# 社区支持
- GitHub Issues: 提交问题和功能建议
- 技术支持: 联系 Zadig 技术支持团队
- 贡献指南: 欢迎向官方插件库贡献优质插件


